package com.jms.framework.web.service;

import javax.annotation.Resource;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.hsoft.sfrz.common.security.client.TysfrzClient4;
import com.jms.common.core.domain.entity.SysDept;
import com.jms.common.core.domain.model.RegisterBody;
import com.jms.common.filter.utils.http.HttpUtils;
import com.jms.system.mapper.SysUserMapper;
import com.jms.system.service.ISysDeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;
import com.jms.common.constant.CacheConstants;
import com.jms.common.constant.Constants;
import com.jms.common.constant.UserConstants;
import com.jms.common.core.domain.entity.SysUser;
import com.jms.common.core.domain.model.LoginUser;
import com.jms.common.core.redis.RedisCache;
import com.jms.common.exception.ServiceException;
import com.jms.common.exception.user.BlackListException;
import com.jms.common.exception.user.CaptchaException;
import com.jms.common.exception.user.CaptchaExpireException;
import com.jms.common.exception.user.UserNotExistsException;
import com.jms.common.exception.user.UserPasswordNotMatchException;
import com.jms.common.filter.utils.DateUtils;
import com.jms.common.filter.utils.MessageUtils;
import com.jms.common.filter.utils.StringUtils;
import com.jms.common.filter.utils.ip.IpUtils;
import com.jms.framework.manager.AsyncManager;
import com.jms.framework.manager.factory.AsyncFactory;
import com.jms.framework.security.context.AuthenticationContextHolder;
import com.jms.system.service.ISysConfigService;
import com.jms.system.service.ISysUserService;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.ResponseBody;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;

/**
 * 登录校验方法
 * 
 * @author drx
 */
@Component
public class SysLoginService
{
    @Autowired
    private TokenService tokenService;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;
    
    @Autowired
    private ISysUserService userService;

    @Autowired
    private ISysConfigService configService;

    @Autowired
    private SysRegisterService registerService;

    @Autowired
    private ISysDeptService sysDeptService;

    @Autowired
    private SysUserMapper userMapper;

    @Autowired
    private SysPermissionService permissionService;


    /**
     * 登录验证
     * 
     * @param username 用户名
     * @param password 密码
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public String login(String username, String password, String code, String uuid)
    {
        // 验证码校验
        validateCaptcha(username, code, uuid);
        // 登录前置校验
        loginPreCheck(username, password);
        // 用户验证
        Authentication authentication = null;
        try
        {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        }
        finally
        {
            AuthenticationContextHolder.clearContext();
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        recordLoginInfo(loginUser.getUserId());
        // 生成token
        return tokenService.createToken(loginUser);
    }

    /**
     * 校验验证码
     * 
     * @param username 用户名
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public void validateCaptcha(String username, String code, String uuid)
    {
        boolean captchaEnabled = configService.selectCaptchaEnabled();
        if (captchaEnabled)
        {
            String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
            String captcha = redisCache.getCacheObject(verifyKey);
            redisCache.deleteObject(verifyKey);
            if (captcha == null)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
                throw new CaptchaExpireException();
            }
            if (!code.equalsIgnoreCase(captcha))
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
                throw new CaptchaException();
            }
        }
    }

    /**
     * 登录前置校验
     * @param username 用户名
     * @param password 用户密码
     */
    public void loginPreCheck(String username, String password)
    {
        // 用户名或密码为空 错误
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
            throw new UserNotExistsException();
        }
        // 密码如果不在指定范围内 错误
        if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
                || password.length() > UserConstants.PASSWORD_MAX_LENGTH)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // 用户名不在指定范围内 错误
        if (username.length() < UserConstants.USERNAME_MIN_LENGTH
                || username.length() > UserConstants.USERNAME_MAX_LENGTH)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
            throw new UserPasswordNotMatchException();
        }
        // IP黑名单校验
        String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
        if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr()))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
            throw new BlackListException();
        }
    }

    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    public void recordLoginInfo(Long userId)
    {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(IpUtils.getIpAddr());
        sysUser.setLoginDate(DateUtils.getNowDate());
        userService.updateUserProfile(sysUser);
    }

    public String loginQSS(String ticket) {
        String token = "";
        System.out.println("-------------------ticket------------"+ticket);

        try {
            //获取签名token
            Map<String, String> params  = new HashMap<>();
            params.put("AccessKey",Constants.ACCESS_KEY);
            params.put("SecretKey",Constants.SECRET_KEY);
            String tokenParams = getQueryUrl(params);
            //设置body参数
            String bodyParams = "&clientId=jmssblztcid&clientSecret=jmssblztcsecret";
            //获取token参数
            String tokenStr = HttpUtils.sendPost(Constants.TOKEN_URL+"?"+tokenParams, bodyParams);
            Map<String, Object> resultMap = JSONObject.parseObject(tokenStr);
            System.out.println("token:"+resultMap);

            int i= (int) resultMap.get("code");
            //失败时，重定向返回
            if(200 != (int) resultMap.get("code") ){
                return "redirect:https://sfrz.zwfw.hlj.gov.cn/sfrz/f/apploginuser/appsysuser/applogin?uuid="+Constants.APP_UUID;
            }
            //获取accessToken
            Map tokenData = (Map)resultMap.get("data");;
            String accessToken = (String) tokenData.get("accessToken");
            //刷新签名
            tokenParams = getQueryUrl(params);
            System.out.println("tokenParams:"+tokenParams);
            ObjectMapper mapper = new ObjectMapper();
            ObjectNode jsonObject = mapper.createObjectNode();
            //加密前准备参数
            String ticketStr = "{ticket:"+"'"+ticket+"'"+"}";
            //加密ticket
            String ticketJM = TysfrzClient4.encrypt(ticketStr,Constants.JM_KEY);
            //获取用户信息
            jsonObject.put("appMark",Constants.APP_MARK);
            jsonObject.put("appSecret",Constants.APP_SECRET);
            jsonObject.put("appUuid",Constants.APP_UUID);
            jsonObject.put("data",ticketJM);
            String jsonString = mapper.writeValueAsString(jsonObject);
            String userStr = HttpUtils.sendJsonPost(Constants.USER_URL+"?"+tokenParams, jsonString.toString(),accessToken);
            Map<String, Object> userMap = JSONObject.parseObject(userStr);
            System.out.println(userMap);
            //失败时，重定向返回
            if(200 != (int) resultMap.get("code") ){
                return "redirect:https://sfrz.zwfw.hlj.gov.cn/sfrz/f/apploginuser/appsysuser/applogin?uuid="+Constants.APP_UUID;
            }
            //判断是否包含data数据，是则进行用户信息解密
            if(userMap.containsKey("data")){
                JSONObject json = (JSONObject) userMap.get("data");
                //非成功情况返回首页
                if(!"000000".equals(json.get("retCode"))){
                    return "redirect:https://sfrz.zwfw.hlj.gov.cn/sfrz/f/apploginuser/appsysuser/applogin?uuid="+Constants.APP_UUID;
                }
                String dataStr = json.get("data").toString();
                //解密用户信息
                String users =  TysfrzClient4.decrypt(dataStr,Constants.JM_KEY);
                Map<String, Object> userInfo = JSONObject.parseObject(users);
                JSONObject userJson = (JSONObject) userInfo.get("userInfo");
                System.out.println("userJson:"+userJson);
                //根据手机号获取用户信息
                SysUser user = userService.selectUserByTel(userJson.getString("userMobile"));
                //根据全省事区划代码查询本系统区划信息
                SysDept sysDept = sysDeptService.selectDeptByAreaShortCode(userJson.getString("areaCode"));
                //当用户信息为空时则判定为企业，注册账号并给予企业角色
                if(null == user){
                    //注册企业账号
                    RegisterBody registerBody = new RegisterBody();
                    //默认账号 手机号
                    registerBody.setUsername(userJson.getString("userMobile"));
                    //默认密码123456
                    registerBody.setPassword(Constants.DEFAULT_PASSWORD);
                    //注册接口
                    registerService.register(registerBody);
                    //注册成功后，查询出用户信息，更新角色、区划等信息
                    user = userService.selectUserByUserName(userJson.getString("userMobile"));
                    //备注为企业
                    user.setRemark("企业");
                    //固定企业角色
                    Long[] roleIds = {3L};
                    user.setRoleIds(roleIds);
                    // 新增用户与角色管理
                    userService.insertUserRole(user);
                }
                //更新手机号
                user.setPhonenumber(userJson.getString("userMobile"));
                //更新系统昵称
                user.setNickName(userJson.getString("userName"));
                //更新系统用户区划
                user.setDeptId(sysDept.getDeptId());
                //更新系统用户信息
                userMapper.updateUser(user);
                //组装用户信息（部门，权限等）
                LoginUser loginUser = (LoginUser) new LoginUser();
                loginUser.setUser(user);
                loginUser.setUserId(loginUser.getUser().getUserId());
                loginUser.setDeptId(loginUser.getUser().getDeptId());
                loginUser.setPermissions(permissionService.getMenuPermission(user));
                //记录用户登录
                recordLoginInfo(loginUser.getUser().getUserId());
                // 生成token
                token = tokenService.createToken(loginUser);
                System.out.println("token:"+token);
            }
            System.out.println("------------token-------："+token);
            return token;
        } catch (Exception e){
            System.out.println("业务逻辑处理失败了。");
            return "redirect:https://sfrz.zwfw.hlj.gov.cn/sfrz/f/apploginuser/appsysuser/applogin?uuid="+Constants.APP_UUID;
        }
    }

    /**
     * 获得query参数，没有传空集合
     *
     * @param params
     * @return
     */
    public static String getQueryUrl(Map<String, String> params) {
        Map<String, String> sorted = new TreeMap<>();
        if (null != params && params.size() > 0) {
            params.forEach((k, v) -> sorted.put(k, v));
        }
        // 海算平台上创建应用的AppKey
        sorted.put("AccessKey",params.get("AccessKey"));
        // 海算平台上创建应用的AppSecret
        sorted.put("SecretKey",params.get("SecretKey"));
        // 时间戳
        sorted.put("timestamp", String.valueOf(System.currentTimeMillis()));
        // 随机数
        Random random = new Random();
        sorted.put("nonce", String.valueOf(random.nextInt()));
        // Query位置的业务参数也需要纳入计算
        // sorted.put("xxx", “xxx”);
        // 最后计算签名
        sorted.put("sign", getSign(sorted));
        sorted.remove("SecretKey");
        StringBuilder builder = new StringBuilder();
        sorted.forEach((k, v) -> {
            try {
                builder.append(k).append("=").append(URLEncoder.encode(v, "utf-8")).append("&");
            } catch (UnsupportedEncodingException e) {
                System.out.println("对参数进行重新编码时出错。");
            }
        });
        builder.deleteCharAt(builder.length()-1);
        System.out.println(builder.toString());
        return builder.toString();
    }
    //计算sign
    public static String getSign(Map<String, String> params) {
        Map<String, String> sorted = new TreeMap<>();
        params.forEach((k, v) -> sorted.put(k, v));
        StringBuilder builder = new StringBuilder();
        sorted.forEach((k, v) -> {
            builder.append(k).append("=").append(v).append("&");
        });
        String value = builder.toString();
        value = value.substring(0, value.length() - 1);
        return DigestUtils.md5DigestAsHex(value.getBytes()).toUpperCase();
    }
}
